/*-------------------------------------------------------------------------
 *
 * print.c
 *	  各种打印例程（主要用于调试）
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/nodes/print.c
 *
 * HISTORY
 *	  AUTHOR			DATE			MAJOR EVENT
 *	  Andrew Yu			Oct 26, 1994	file creation
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include "access/printtup.h"
#include "lib/stringinfo.h"
#include "nodes/nodeFuncs.h"
#include "nodes/pathnodes.h"
#include "nodes/print.h"
#include "parser/parsetree.h"
#include "utils/lsyscache.h"


/*
 * print
 *	  打印 Node 的内容到标准输出
 */
void print(const void *fc_obj)
{
	char	   *fc_s;
	char	   *fc_f;

	fc_s = nodeToString(fc_obj);
	fc_f = format_node_dump(fc_s);
	pfree(fc_s);
	printf("%s\n", fc_f);
	fflush(stdout);
	pfree(fc_f);
}

/*
 * pprint
 *	  美观地打印 Node 的内容到标准输出
 */
void pprint(const void *fc_obj)
{
	char	   *fc_s;
	char	   *fc_f;

	fc_s = nodeToString(fc_obj);
	fc_f = pretty_format_node_dump(fc_s);
	pfree(fc_s);
	printf("%s\n", fc_f);
	fflush(stdout);
	pfree(fc_f);
}

/*
 * elog_node_display
 *	  将美观打印的 Node 内容发送到 postmaster 日志
 */
void elog_node_display(int fc_lev, const char *fc_title, const void *fc_obj, bool fc_pretty)
{
	char	   *fc_s;
	char	   *fc_f;

	fc_s = nodeToString(fc_obj);
	if (fc_pretty)
		fc_f = pretty_format_node_dump(fc_s);
	else
		fc_f = format_node_dump(fc_s);
	pfree(fc_s);
	ereport(fc_lev,
			(errmsg_internal("%s:", fc_title),
			 errdetail_internal("%s", fc_f)));
	pfree(fc_f);
}

/*
 * 为终端显示格式化 nodeToString 输出。
 *
 * 结果是一个 palloc'd 字符串。
 *
 * 这个版本只是尝试在空白处断开。
 */
char * format_node_dump(const char *fc_dump)
{
#define LINELEN		78
	char		fc_line[LINELEN + 1];
	StringInfoData fc_str;
	int			fc_i;
	int			fc_j;
	int			fc_k;

	initStringInfo(&fc_str);
	fc_i = 0;
	for (;;)
	{
		for (fc_j = 0; fc_j < LINELEN && fc_dump[fc_i] != '\0'; fc_i++, fc_j++)
			fc_line[fc_j] = fc_dump[fc_i];
		if (fc_dump[fc_i] == '\0')
			break;
		if (fc_dump[fc_i] == ' ')
		{
			/* 可以在相邻空格处断开 */
			fc_i++;
		}
		else
		{
			for (fc_k = fc_j - 1; fc_k > 0; fc_k--)
				if (fc_line[fc_k] == ' ')
					break;
			if (fc_k > 0)
			{
				/* 回退；将重新打印空格后的所有内容 */
				fc_i -= (fc_j - fc_k - 1);
				fc_j = fc_k;
			}
		}
		fc_line[fc_j] = '\0';
		appendStringInfo(&fc_str, "%s\n", fc_line);
	}
	if (fc_j > 0)
	{
		fc_line[fc_j] = '\0';
		appendStringInfo(&fc_str, "%s\n", fc_line);
	}
	return fc_str.data;
#undef LINELEN
}

/*
 * 为终端显示格式化 nodeToString 输出。
 *
 * 结果是一个 palloc'd 字符串。
 *
 * 这个版本试图智能缩进。
 */
char * pretty_format_node_dump(const char *fc_dump)
{
#define INDENTSTOP	3
#define MAXINDENT	60
#define LINELEN		78
	char		fc_line[LINELEN + 1];
	StringInfoData fc_str;
	int			fc_indentLev;
	int			fc_indentDist;
	int			fc_i;
	int			fc_j;

	initStringInfo(&fc_str);
	fc_indentLev = 0;				/* 逻辑缩进级别 */
	fc_indentDist = 0;				/* 物理缩进距离 */
	fc_i = 0;
	for (;;)
	{
		for (fc_j = 0; fc_j < fc_indentDist; fc_j++)
			fc_line[fc_j] = ' ';
		for (; fc_j < LINELEN && fc_dump[fc_i] != '\0'; fc_i++, fc_j++)
		{
			fc_line[fc_j] = fc_dump[fc_i];
			switch (fc_line[fc_j])
			{
				case '}':
					if (fc_j != fc_indentDist)
					{
						/* 在 } 之前打印数据 */
						fc_line[fc_j] = '\0';
						appendStringInfo(&fc_str, "%s\n", fc_line);
					}
					/* 在 indentDist 处打印 } */
					fc_line[fc_indentDist] = '}';
					fc_line[fc_indentDist + 1] = '\0';
					appendStringInfo(&fc_str, "%s\n", fc_line);
					/* outdent */
					if (fc_indentLev > 0)
					{
						fc_indentLev--;
						fc_indentDist = Min(fc_indentLev * INDENTSTOP, MAXINDENT);
					}
					fc_j = fc_indentDist - 1;
					/* j 将在下一次循环迭代中等于 indentDist */
					/* 抑制 } 之后的空白 */
					while (fc_dump[fc_i + 1] == ' ')
						fc_i++;
					break;
				case ')':
					/* 在 ）之后强制换行，除非后面跟着另一个 ） */
					if (fc_dump[fc_i + 1] != ')')
					{
						fc_line[fc_j + 1] = '\0';
						appendStringInfo(&fc_str, "%s\n", fc_line);
						fc_j = fc_indentDist - 1;
						while (fc_dump[fc_i + 1] == ' ')
							fc_i++;
					}
					break;
				case '{':
					/* 在 { 之前强制换行 */
					if (fc_j != fc_indentDist)
					{
						fc_line[fc_j] = '\0';
						appendStringInfo(&fc_str, "%s\n", fc_line);
					}
					/* indent */
					fc_indentLev++;
					fc_indentDist = Min(fc_indentLev * INDENTSTOP, MAXINDENT);
					for (fc_j = 0; fc_j < fc_indentDist; fc_j++)
						fc_line[fc_j] = ' ';
					fc_line[fc_j] = fc_dump[fc_i];
					break;
				case ':':
					/* 在 : 之前强制换行 */
					if (fc_j != fc_indentDist)
					{
						fc_line[fc_j] = '\0';
						appendStringInfo(&fc_str, "%s\n", fc_line);
					}
					fc_j = fc_indentDist;
					fc_line[fc_j] = fc_dump[fc_i];
					break;
			}
		}
		fc_line[fc_j] = '\0';
		if (fc_dump[fc_i] == '\0')
			break;
		appendStringInfo(&fc_str, "%s\n", fc_line);
	}
	if (fc_j > 0)
		appendStringInfo(&fc_str, "%s\n", fc_line);
	return fc_str.data;
#undef INDENTSTOP
#undef MAXINDENT
#undef LINELEN
}

/*
 * print_rt
 *	  打印范围表的内容
 */
void print_rt(const List *fc_rtable)
{
	const ListCell *fc_l;
	int			fc_i = 1;

	printf("resno\trefname  \trelid\tinFromCl\n");
	printf("-----\t---------\t-----\t--------\n");
	foreach(fc_l, fc_rtable)
	{
		RangeTblEntry *fc_rte = lfirst(fc_l);

		switch (fc_rte->rtekind)
		{
			case RTE_RELATION:
				printf("%d\t%s\t%u\t%c",
					   fc_i, fc_rte->eref->aliasname, fc_rte->relid, fc_rte->relkind);
				break;
			case RTE_SUBQUERY:
				printf("%d\t%s\t[subquery]",
					   fc_i, fc_rte->eref->aliasname);
				break;
			case RTE_JOIN:
				printf("%d\t%s\t[join]",
					   fc_i, fc_rte->eref->aliasname);
				break;
			case RTE_FUNCTION:
				printf("%d\t%s\t[rangefunction]",
					   fc_i, fc_rte->eref->aliasname);
				break;
			case RTE_TABLEFUNC:
				printf("%d\t%s\t[table function]",
					   fc_i, fc_rte->eref->aliasname);
				break;
			case RTE_VALUES:
				printf("%d\t%s\t[values list]",
					   fc_i, fc_rte->eref->aliasname);
				break;
			case RTE_CTE:
				printf("%d\t%s\t[cte]",
					   fc_i, fc_rte->eref->aliasname);
				break;
			case RTE_NAMEDTUPLESTORE:
				printf("%d\t%s\t[tuplestore]",
					   fc_i, fc_rte->eref->aliasname);
				break;
			case RTE_RESULT:
				printf("%d\t%s\t[result]",
					   fc_i, fc_rte->eref->aliasname);
				break;
			default:
				printf("%d\t%s\t[unknown rtekind]",
					   fc_i, fc_rte->eref->aliasname);
		}

		printf("\t%s\t%s\n",
			   (fc_rte->inh ? "inh" : ""),
			   (fc_rte->inFromCl ? "inFromCl" : ""));
		fc_i++;
	}
}


/*
 * print_expr
 *	  打印一个表达式
 */
void print_expr(const Node *fc_expr, const List *fc_rtable)
{
	if (fc_expr == NULL)
	{
		printf("<>");
		return;
	}

	if (IsA(fc_expr, Var))
	{
		const Var  *fc_var = (const Var *) fc_expr;
		char	   *fc_relname,
				   *fc_attname;

		switch (fc_var->varno)
		{
			case INNER_VAR:
				fc_relname = "INNER";
				fc_attname = "?";
				break;
			case OUTER_VAR:
				fc_relname = "OUTER";
				fc_attname = "?";
				break;
			case INDEX_VAR:
				fc_relname = "INDEX";
				fc_attname = "?";
				break;
			default:
				{
					RangeTblEntry *fc_rte;

					Assert(fc_var->varno > 0 &&
						   (int) fc_var->varno <= list_length(fc_rtable));
					fc_rte = rt_fetch(fc_var->varno, fc_rtable);
					fc_relname = fc_rte->eref->aliasname;
					fc_attname = get_rte_attribute_name(fc_rte, fc_var->varattno);
				}
				break;
		}
		printf("%s.%s", fc_relname, fc_attname);
	}
	else if (IsA(fc_expr, Const))
	{
		const Const *fc_c = (const Const *) fc_expr;
		Oid			fc_typoutput;
		bool		fc_typIsVarlena;
		char	   *fc_outputstr;

		if (fc_c->constisnull)
		{
			printf("NULL");
			return;
		}

		getTypeOutputInfo(fc_c->consttype,
						  &fc_typoutput, &fc_typIsVarlena);

		fc_outputstr = OidOutputFunctionCall(fc_typoutput, fc_c->constvalue);
		printf("%s", fc_outputstr);
		pfree(fc_outputstr);
	}
	else if (IsA(fc_expr, OpExpr))
	{
		const OpExpr *fc_e = (const OpExpr *) fc_expr;
		char	   *fc_opname;

		fc_opname = get_opname(fc_e->opno);
		if (list_length(fc_e->args) > 1)
		{
			print_expr(get_leftop((const Expr *) fc_e), fc_rtable);
			printf(" %s ", ((fc_opname != NULL) ? fc_opname : "(invalid operator)"));
			print_expr(get_rightop((const Expr *) fc_e), fc_rtable);
		}
		else
		{
			printf("%s ", ((fc_opname != NULL) ? fc_opname : "(invalid operator)"));
			print_expr(get_leftop((const Expr *) fc_e), fc_rtable);
		}
	}
	else if (IsA(fc_expr, FuncExpr))
	{
		const FuncExpr *fc_e = (const FuncExpr *) fc_expr;
		char	   *fc_funcname;
		ListCell   *fc_l;

		fc_funcname = get_func_name(fc_e->funcid);
		printf("%s(", ((fc_funcname != NULL) ? fc_funcname : "(invalid function)"));
		foreach(fc_l, fc_e->args)
		{
			print_expr(lfirst(fc_l), fc_rtable);
			if (lnext(fc_e->args, fc_l))
				printf(",");
		}
		printf(")");
	}
	else
		printf("unknown expr");
}

/*
 * print_pathkeys -
 *	  PathKeys 的 pathkeys 列表
 */
void print_pathkeys(const List *fc_pathkeys, const List *fc_rtable)
{
	const ListCell *fc_i;

	printf("(");
	foreach(fc_i, fc_pathkeys)
	{
		PathKey    *fc_pathkey = (PathKey *) lfirst(fc_i);
		EquivalenceClass *fc_eclass;
		ListCell   *fc_k;
		bool		fc_first = true;

		fc_eclass = fc_pathkey->pk_eclass;
		/* 追溯，在路径键非规范的情况下 */
		while (fc_eclass->ec_merged)
			fc_eclass = fc_eclass->ec_merged;

		printf("(");
		foreach(fc_k, fc_eclass->ec_members)
		{
			EquivalenceMember *fc_mem = (EquivalenceMember *) lfirst(fc_k);

			if (fc_first)
				fc_first = false;
			else
				printf(", ");
			print_expr((Node *) fc_mem->em_expr, fc_rtable);
		}
		printf(")");
		if (lnext(fc_pathkeys, fc_i))
			printf(", ");
	}
	printf(")\n");
}

/*
 * print_tl
 *	  以更易读的方式打印目标列表。
 */
void print_tl(const List *fc_tlist, const List *fc_rtable)
{
	const ListCell *fc_tl;

	printf("(\n");
	foreach(fc_tl, fc_tlist)
	{
		TargetEntry *fc_tle = (TargetEntry *) lfirst(fc_tl);

		printf("\t%d %s\t", fc_tle->resno,
			   fc_tle->resname ? fc_tle->resname : "<null>");
		if (fc_tle->ressortgroupref != 0)
			printf("(%u):\t", fc_tle->ressortgroupref);
		else
			printf("    :\t");
		print_expr((Node *) fc_tle->expr, fc_rtable);
		printf("\n");
	}
	printf(")\n");
}

/*
 * print_slot
 *	  打印给定 TupleTableSlot 的元组
 */
void print_slot(TupleTableSlot *fc_slot)
{
	if (TupIsNull(fc_slot))
	{
		printf("tuple is null.\n");
		return;
	}
	if (!fc_slot->tts_tupleDescriptor)
	{
		printf("no tuple descriptor.\n");
		return;
	}

	debugtup(fc_slot, NULL);
}
